﻿/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** FileX Component                                                       */
/**                                                                       */
/**   SPIM Disk Driver                                                     */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/


/* Include necessary system files.  */

#include "tx_api.h"
#include "fx_api.h"
#include "bsp_spim.h"
#include "bsp_hal.h"
#include "mytype.h"


VOID _fx_spim_driver(FX_MEDIA *media_ptr);

VOID  _fx_spim_driver(FX_MEDIA *media_ptr)
{
    /* There are several useful/important pieces of information contained in 
       the media structure, some of which are supplied by FileX and others 
       are for the driver to setup. The following is a summary of the 
       necessary FX_MEDIA structure members:

            FX_MEDIA Member                    Meaning

        fx_media_driver_request             FileX request type. Valid requests from 
                                            FileX are as follows:

                                                    FX_DRIVER_READ
                                                    FX_DRIVER_WRITE
                                                    FX_DRIVER_FLUSH
                                                    FX_DRIVER_ABORT
                                                    FX_DRIVER_INIT
                                                    FX_DRIVER_BOOT_READ
                                                    FX_DRIVER_RELEASE_SECTORS
                                                    FX_DRIVER_BOOT_WRITE
                                                    FX_DRIVER_UNINIT

        fx_media_driver_status              This value is RETURNED by the driver. 
                                            If the operation is successful, this 
                                            field should be set to FX_SUCCESS for 
                                            before returning. Otherwise, if an 
                                            error occurred, this field should be 
                                            set to FX_IO_ERROR.

        fx_media_driver_buffer              Pointer to buffer to read or write 
                                            sector data. This is supplied by 
                                            FileX.

        fx_media_driver_logical_sector      Logical sector FileX is requesting.

        fx_media_driver_sectors             Number of sectors FileX is requesting.


       The following is a summary of the optional FX_MEDIA structure members:

            FX_MEDIA Member                              Meaning

        fx_media_driver_info                Pointer to any additional information 
                                            or memory. This is optional for the 
                                            driver use and is setup from the 
                                            fx_media_open call. The RAM disk uses
                                            this pointer for the RAM disk memory 
                                            itself.

        fx_media_driver_write_protect       The DRIVER sets this to FX_TRUE when 
                                            media is write protected. This is 
                                            typically done in initialization,
                                            but can be done anytime.

        fx_media_driver_free_sector_update  The DRIVER sets this to FX_TRUE when 
                                            it needs to know when clusters are 
                                            released. This is important for FLASH 
                                            wear-leveling drivers.

        fx_media_driver_system_write        FileX sets this flag to FX_TRUE if the 
                                            sector being written is a system sector, 
                                            e.g., a boot, FAT, or directory sector. 
                                            The driver may choose to use this to 
                                            initiate error recovery logic for greater
                                            fault tolerance.

        fx_media_driver_data_sector_read    FileX sets this flag to FX_TRUE if the 
                                            sector(s) being read are file data sectors, 
                                            i.e., NOT system sectors.

        fx_media_driver_sector_type         FileX sets this variable to the specific 
                                            type of sector being read or written. The 
                                            following sector types are identified:

                                                    FX_UNKNOWN_SECTOR
                                                    FX_BOOT_SECTOR
                                                    FX_FAT_SECTOR
                                                    FX_DIRECTORY_SECTOR
                                                    FX_DATA_SECTOR
     */

    /* Process the driver request specified in the media control block.  */
    switch (media_ptr -> fx_media_driver_request)
    {

    case FX_DRIVER_READ:
    {
        uu32 address;
        /*---------------------------------------------------------*
        * FileX通过向I/O驱动程序发出读取请求，将一个或多个扇区读入内存
        * 以下介质成员用于I/O驱动程序读取请求 ：
        * fx_media_driver_logical_sector：逻辑扇区号
        * fx_media_driver_sectors：读取扇区数
        * fx_media_driver_buffer：数据缓存
        * fx_media_driver_data_sector_read：
        * fx_media_driver_sector_type：扇区类型
        * fx_media_hidden_sectors :隐藏扇区数
        * fx_media_bytes_per_sector :每个扇区字节数
        *---------------------------------------------------------*/
        
        //spim data address creat
        address =(media_ptr -> fx_media_driver_logical_sector + media_ptr -> fx_media_hidden_sectors) 
            * media_ptr -> fx_media_bytes_per_sector;
        
        address += SPIM_START_ADDR;
        
        //read data from spim
        _fx_utility_memory_copy((uu8*)address, 
                                media_ptr -> fx_media_driver_buffer, 
                                media_ptr -> fx_media_driver_sectors * media_ptr -> fx_media_bytes_per_sector);
        
        //read ok
        media_ptr -> fx_media_driver_status =  FX_SUCCESS;
        break;
    }

    case FX_DRIVER_WRITE:
    {
        /*---------------------------------------------------------*
        * FileX通过向I/O驱动程序发出写入请求，将一个或多个扇区写入物理介质。
        * 以下介质成员用于I/O驱动程序读取请求 ：
        * fx_media_driver_logical_sector：逻辑扇区号
        * fx_media_driver_sectors：读取扇区数
        * fx_media_driver_buffer：数据缓存
        * fx_media_driver_data_sector_read：
        * fx_media_driver_sector_type：扇区类型
        * fx_media_hidden_sectors :隐藏扇区数
        * fx_media_bytes_per_sector :每个扇区字节数
        *---------------------------------------------------------*/
        uint32_t address , cnt, amount, *pdat;
        
        //spim data address creat
        address =(media_ptr -> fx_media_driver_logical_sector + media_ptr -> fx_media_hidden_sectors) 
            * media_ptr -> fx_media_bytes_per_sector;
        address += SPIM_START_ADDR;
        
        //data amount and src
        amount = media_ptr -> fx_media_driver_sectors * media_ptr -> fx_media_bytes_per_sector;
        pdat = (uint32_t *)media_ptr -> fx_media_driver_buffer;
        
        //program to spim
        FLASH_UnlockBank3();
        
        FLASH_ErasePage(address);
        
        for(cnt=0; cnt < amount; cnt+=4)
        {
            FLASH_ProgramWord(address + cnt, *pdat++);
        }

        FLASH_LockBank3();   
        
        //write ok
        media_ptr -> fx_media_driver_status =  FX_SUCCESS;
        break;
    }

    case FX_DRIVER_FLUSH:
    {

        /*---------------------------------------------------------*
        * FileX通过向I/O驱动程序发出刷新请求，将当前在驱动程序扇区缓存中的所有扇区刷新到物理介质。
        * 当然，如果驱动程序没有缓存扇区，则此请求不需要驱动程序处理。
        *---------------------------------------------------------*/
        media_ptr -> fx_media_driver_status =  FX_SUCCESS;
        break;
    }

    case FX_DRIVER_ABORT:
    {
        /*---------------------------------------------------------
        * FileX通过向I/O驱动程序发出中止请求，通知驱动程序中止物理介质的所有进一步的物理I/O活动。
        * 在重新初始化之前，驱动程序不应再次执行任何I/O。
        ---------------------------------------------------------*/
        media_ptr -> fx_media_driver_status =  FX_SUCCESS;
        break;
    }

    case FX_DRIVER_INIT:
    {

        /* FLASH drivers are responsible for setting several fields in the
           media structure, as follows:

                media_ptr -> fx_media_driver_free_sector_update
                media_ptr -> fx_media_driver_write_protect

           The fx_media_driver_free_sector_update flag is used to instruct
           FileX to inform the driver whenever sectors are not being used.
           This is especially useful for FLASH managers so they don't have
           maintain mapping for sectors no longer in use.

           The fx_media_driver_write_protect flag can be set anytime by the
           driver to indicate the media is not writable.  Write attempts made
           when this flag is set are returned as errors.  */

        /* Perform basic initialization here... since the boot record is going
           to be read subsequently and again for volume name requests.  */

        /* Successful driver request.  */
        media_ptr -> fx_media_driver_status =  FX_SUCCESS;
        break;
    }

    case FX_DRIVER_UNINIT:
    {

        /* There is nothing to do in this case for the RAM driver.  For actual
           devices some shutdown processing may be necessary.  */

        /* Successful driver request.  */
        media_ptr -> fx_media_driver_status =  FX_SUCCESS;
        break;
    }

    case FX_DRIVER_BOOT_READ:
    {
        /*---------------------------------------------------------
        * FileX不使用标准的读取请求，而是发出一个特定的请求来读取媒体的引导扇区。
        * 以下FX\ U介质成员用于I/O驱动程序引导扇区读取请求： 
        * fx_media_driver_info    :媒体信息，由fx_media_open指定，RAM模式代表首地址
        * fx_media_driver_buffer  :读取数据缓存
        *---------------------------------------------------------*/
        //spim address creat
        uu8 *src = (uu8 *)SPIM_START_ADDR;
        
        //check disk info
        if ((src[0] != (UCHAR)0xEB)  
            || ((src[1] != (UCHAR)0x34)  && (src[1] != (UCHAR)0x76))           /* exFAT jump code.  */
            || (src[2] != (UCHAR)0x90))
        {
            /* Invalid boot record, return an error!  */
            media_ptr -> fx_media_driver_status =  FX_MEDIA_INVALID;
            return;
        }

#ifdef FX_ENABLE_EXFAT
        /* if byte per sector is zero, then treat it as exFAT volume.  */
        if (bytes_per_sector == 0 && (src[1] == (UCHAR)0x76))
        {
            /* Pickup the byte per sector shift, and calculate byte per sector.  */
            bytes_per_sector = (UINT)(1 << src[FX_EF_BYTE_PER_SECTOR_SHIFT]);
        }
#endif /* FX_ENABLE_EXFAT */

        //buffer info check， page size can't use fx_media_bytes_per_sector 
        if (SPIM_PAGE_SIZE > media_ptr -> fx_media_memory_size)
        {
            media_ptr -> fx_media_driver_status =  FX_BUFFER_ERROR;
            break;
        }

        //read data from spim
        _fx_utility_memory_copy(src, 
                                media_ptr -> fx_media_driver_buffer,
                                SPIM_PAGE_SIZE);

        //read ok
        media_ptr -> fx_media_driver_status =  FX_SUCCESS;
        break;
    }

    case FX_DRIVER_BOOT_WRITE:
    {
        uu32 address, cnt, amount, *pdat;
        
        /*---------------------------------------------------------
        * FileX不使用标准的写入请求，而是发出一个特定的请求来写入媒体的引导扇区。
        * 以下媒体成员用于I/O驱动程序引导扇区写入请求 
        * fx_media_driver_info    :媒体信息，由fx_media_open指定，RAM模式代表首地址
        * fx_media_driver_buffer  :写入数据缓存
        *---------------------------------------------------------*/
        
        //spim address creat
        address = SPIM_START_ADDR;
        
        amount = media_ptr -> fx_media_bytes_per_sector;
        pdat = (uu32 *)media_ptr -> fx_media_driver_buffer;
        
        //program to spim
        FLASH_UnlockBank3();
        
        FLASH_ErasePage(address);
        
        for(cnt=0; cnt < amount; cnt+=4)
        {
            FLASH_ProgramWord(address + cnt, *pdat++);
        }

        FLASH_LockBank3(); 
        
        //write ok
        media_ptr -> fx_media_driver_status =  FX_SUCCESS;
        break;
    }

    default:
    {

        /* Invalid driver request.  */
        media_ptr -> fx_media_driver_status =  FX_IO_ERROR;
        break;
    }
    }
}

